# -*- coding: utf-8 -*-

import os
import time
import logging
import traceback

from flask import render_template, current_app, jsonify
from flask_restful import Resource, marshal_with, fields, reqparse, inputs
from flask_login import login_required, current_user

from sqlalchemy import or_
from sqlalchemy.ext.serializer import dumps

from . import models
from . import manager, manager_api
from config import BASE_PATH
from wxgame import db

logger = logging.getLogger(__name__)


@manager.route('/player/')
@login_required
def players():
    return render_template('manager/player_list.html', page='players')


@manager.route('/player/<id_>')
@login_required
def player(id_):
    return render_template('manager/player_detail.html', player_wxid=id_, page='players')


@manager.route('/')
@login_required
def gamesets():
    return render_template('manager/gameset_list.html', page='gamesets')


@manager.route('/<int:id_>')
@login_required
def gameset(id_):
    return render_template('manager/gameset_detail.html', gameset_id=id_, page='gamesets')


@manager.route('/agent')
@login_required
def agent():
    return render_template('manager/agent.html', page='agent')


@manager.route('/except_grabber')
@login_required
def except_grabber():
    return render_template('manager/except_grabber.html', page='except_grabber')


@manager.route('/financing')
@login_required
def financing():
    return render_template('manager/financing.html', page='financing')


@manager.route('/ranking/point')
@login_required
def ranking_by_point():
    return render_template('manager/ranking_by_point.html', page='ranking_by_point')


@manager.route('/ranking/wager')
@login_required
def ranking_by_wager():
    return render_template('manager/ranking_by_wager.html', page='ranking_by_wager')


@manager.route('/api/clear', methods=['POST'])
@login_required
def clear():
    try:
        if not current_user.is_admin:
            return jsonify({'error': '仅限管理员操作'}), 403
        ts = int(time.time())
        data_path = os.path.join(BASE_PATH, current_app.config.get('DATA_PATH', 'data'), str(ts))
        if not os.path.exists(data_path):
            os.makedirs(data_path)

        db.engine.execute('set foreign_key_checks=0')
        db.engine.execute(models.Transaction.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '0.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.Statistics.query.all()))
        db.engine.execute(models.Statistics.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '1.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.Gameset.query.all()))
        db.engine.execute(models.Gameset.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '2.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.PlayerGameset.query.all()))
        db.engine.execute(models.PlayerGameset.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '3.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.Player.query.all()))
        db.engine.execute(models.Player.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '4.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.RoomPlayer.query.all()))
        db.engine.execute(models.RoomPlayer.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '5.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.Room.query.all()))
        db.engine.execute(models.Room.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '6.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.Robot.query.all()))
        db.engine.execute(models.Robot.__mapper__.tables[0].delete())

        filepath = os.path.join(data_path, '7.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.RoomRobot.query.all()))
        db.engine.execute(models.RoomRobot.__mapper__.tables[0].delete())

        db.engine.execute('set foreign_key_checks=1')
    except:
        print(traceback.format_exc())
        return jsonify({'error': '清除数据错误，请联系管理员'}), 400
    else:
        return jsonify({'error': None})


@manager.route('/api/clear_wager', methods=['POST'])
@login_required
def clear_wager():
    try:
        if not current_user.is_admin:
            return jsonify({'error': '仅限管理员操作'}), 403
        ts = int(time.time())
        data_path = os.path.join(BASE_PATH, current_app.config.get('DATA_PATH', 'data'), str(ts))
        if not os.path.exists(data_path):
            os.makedirs(data_path)

        db.engine.execute('set foreign_key_checks=0')
        filepath = os.path.join(data_path, '3.bin')
        with open(filepath, 'wb') as f:
            f.write(dumps(models.Player.query.all()))
        models.Player.query.update(
            {'wager_count': 0, 'wager_amount': 0},
            synchronize_session=False
        )
        try:
            db.session.commit()
        except:
            db.session.rollback()
            print(traceback.format_exc())
            return jsonify({'error': '清除数据错误，请联系管理员'}), 400

        db.engine.execute('set foreign_key_checks=1')
    except:
        print(traceback.format_exc())
        return jsonify({'error': '清除数据错误，请联系管理员'}), 400
    else:
        return jsonify({'error': None})


@manager.route('/api/clear_stat', methods=['POST'])
@login_required
def clear_stat():
    try:
        if not current_user.is_admin:
            return jsonify({'error': '仅限管理员操作'}), 403
        ts = int(time.time())
        data_path = os.path.join(BASE_PATH, current_app.config.get('DATA_PATH', 'data'), str(ts))
        if not os.path.exists(data_path):
            os.makedirs(data_path)

        stat = models.Statistics.query.filter_by(status=1).first()
        if stat is not None:
            stat.status = 0

            try:
                db.session.commit()
            except:
                db.session.rollback()
                return jsonify({'error': '清除数据错误，请联系管理员'}), 400
    except:
        print(traceback.format_exc())
        return jsonify({'error': '清除数据错误，请联系管理员'}), 400
    else:
        return jsonify({'error': None})


player_list_field = {
    'players': fields.List(fields.Nested(models.Marshal.player)),
    'count': fields.Integer,
}


class PlayerCollection(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(player_list_field)
    def get(self):
        self.parser.add_argument(
            'page',
            type=inputs.natural,
            default=0
        )
        self.parser.add_argument(
            'qs',
            default=''
        )
        self.parser.add_argument(
            'sortOrder',
            default='descending'
        )
        self.parser.add_argument(
            'sortTarget',
            default='totalPoint'
        )
        args = self.parser.parse_args()
        page = args['page']
        qs = args['qs']
        sort_order = args['sortOrder']
        sort_target = args['sortTarget']
        page_len = current_app.config['PAGE_LENGTH']

        sort_factor = None
        if sort_target == 'wagerCount':
            sort_factor = models.Player.wager_count
        elif sort_target == 'wagerAmount':
            sort_factor = models.Player.wager_amount
        elif sort_target == 'earnedAmount':
            sort_factor = models.Player.earned_amount
        elif sort_target == 'upPoint':
            sort_factor = models.Player.up_point
        elif sort_target == 'downPoint':
            sort_factor = models.Player.down_point
        else:
            sort_factor = models.Player.earned_amount + models.Player.up_point - models.Player.down_point

        if sort_order == 'descending':
            sort_factor = sort_factor.desc()

        if qs:
            criteria = or_(
                models.Player.nickname.like('%{}%'.format(qs)),
                models.Player.alias.like('%{}%'.format(qs)),
                models.Player.wxid.like('%{}%'.format(qs))
            )
            count = models.Player.query.filter(criteria).count()
            players = models.Player.query.filter(criteria).order_by(sort_factor).limit(page_len).offset(page * page_len).all()
        else:
            count = models.Player.query.count()
            players = models.Player.query.order_by(sort_factor).limit(page_len).offset(page*page_len).all()
        return {'players': players, 'count': count}


manager_api.add_resource(PlayerCollection, '/api/player/', endpoint='api_players')


player_field = {
    'player': fields.Nested(models.Marshal.player),
    'count': fields.Integer,
    'gamesets': fields.List(fields.Nested(models.Marshal.player_gameset))
}


class Player(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(player_field)
    def get(self, id_):
        self.parser.add_argument(
            'page',
            type=inputs.natural,
            default=0
        )
        args = self.parser.parse_args()
        page = args['page']
        page_len = current_app.config['PAGE_LENGTH']

        player = models.Player.query.filter_by(wxid=id_).first()
        if player is None:
            return {"message": "No item found"}, 400
        gamesets_qs = models.PlayerGameset.query.filter_by(player_wxid=id_)
        count = gamesets_qs.count()
        gamesets = gamesets_qs.limit(page_len).offset(page*page_len).all()
        return {'player': player, 'count': count, 'gamesets': gamesets}

    def put(self, id_):
        if not current_user.is_admin:
            return {'message': '仅限管理员操作'}, 403
        self.parser.add_argument(
            'upPoint',
            type=int,
            default=None,
            location='get_json',
        )
        self.parser.add_argument(
            'downPoint',
            type=int,
            default=None,
            location='get_json',
        )
        args = self.parser.parse_args()
        up_point = args['upPoint']
        down_point = args['downPoint']
        if up_point is None and down_point is None:
            return {"message": "data error"}, 400

        player = models.Player.query.filter_by(wxid=id_).first()
        stat = models.Statistics.get_instance()
        if player is None:
            return {"message": "No item found"}, 400

        if up_point is not None:
            player.up_point += up_point
            stat.up_point += up_point
            down_point = 0
        else:
            player.down_point += down_point
            stat.down_point += down_point
            up_point = 0

        stat.total_point = stat.total_point + up_point - down_point

        try:
            db.session.commit()
        except:
            db.session.rollback()
            return {"message": "DB error"}, 400

        return {}


manager_api.add_resource(Player, '/api/player/<id_>', endpoint='api_player')


gameset_list_field = {
    'gamesets': fields.List(fields.Nested(models.Marshal.gameset)),
    'count': fields.Integer,
}


class GamesetCollection(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(gameset_list_field)
    def get(self):
        self.parser.add_argument(
            'page',
            type=inputs.natural,
            default=0
        )
        args = self.parser.parse_args()
        page = args['page']
        page_len = current_app.config['PAGE_LENGTH']

        count = models.Gameset.query.count()
        gamesets = models.Gameset.query.order_by(models.Gameset.sn.desc()).limit(page_len).offset(page*page_len).all()
        return {'gamesets': gamesets, 'count': count}


manager_api.add_resource(GamesetCollection, '/api/gameset/', endpoint='api_gamesets')


gameset_field = {
    'gameset': fields.Nested(models.Marshal.gameset),
    'banker': fields.Integer,
    'count': fields.Integer,
    'players': fields.List(fields.Nested(models.Marshal.player_gameset))
}


class Gameset(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(gameset_field)
    def get(self, id_):
        self.parser.add_argument(
            'page',
            type=inputs.natural,
            default=0
        )
        args = self.parser.parse_args()
        page = args['page']
        page_len = current_app.config['PAGE_LENGTH']

        gameset = models.Gameset.query.filter_by(id=id_).first()
        if gameset is None:
            return {"message": "No item found"}, 400
        players_qs = models.PlayerGameset.query.filter_by(gameset_id=id_)
        count = players_qs.count()
        players = players_qs.limit(page_len).offset(page*page_len).all()
        return {'gameset': gameset, 'count': count, 'players': players}


manager_api.add_resource(Gameset, '/api/gameset/<id_>', endpoint='api_gameset')


player_gameset_list_field = {
    'playerGamesets': fields.List(fields.Nested(models.Marshal.player_gameset)),
    'count': fields.Integer
}


class PlayerGameset(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(player_gameset_list_field)
    def get(self, id):
        self.parser.add_argument(
            'page',
            type=inputs.natural,
            default=0
        )
        args = self.parser.parse_args()
        page = args['page']
        page_len = current_app.config['PAGE_LENGTH']
        count = models.Gameset.query.count()
        player_gamesets = models.PlayerGameset.query.limit(page_len).offset(page*page_len).all()
        return {'playerGamesets': player_gamesets, 'count': count}


manager_api.add_resource(PlayerGameset, '/api/playergameset/<id_>', endpoint='api_playergameset')


agent_list_field = {
    'agents': fields.List(fields.Nested(models.Marshal.agent)),
    'count': fields.Integer,
    'totalWager': fields.Integer,
}


class AgentCollection(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(agent_list_field)
    def get(self):
        agents = models.Agent.query.all()
        total_wager = sum(agent.agented_wager_amount for agent in agents)
        return {'agents': agents, 'count': len(agents), 'totalWager': total_wager}

    def post(self):
        if not current_user.is_admin:
            return {'message': '仅限管理员操作'}, 403
        self.parser.add_argument(
            'wxid',
            type=str,
            required=True,
            location='get_json',
        )
        args = self.parser.parse_args()
        wxid = args['wxid']
        success, agents_or_msg = models.Agent.create(wxid)
        if not success:
            return {"message": agents_or_msg}, 400
        return {"message": "add agent successfully"}

    def delete(self):
        if not current_user.is_admin:
            return {'message': '仅限管理员操作'}, 403
        self.parser.add_argument(
            'wxid',
            type=str,
            required=True,
            location='get_json',
        )
        args = self.parser.parse_args()
        wxid = args['wxid']
        agent = models.Agent.query.filter(models.Agent.wxid == wxid).first()
        if agent is None:
            return {"message": 'not an agent'}, 403
        error = agent.revoke_agent()
        if error:
            return {"message": error}, 500
        else:
            return {"message": "revoke agent successfully"}


manager_api.add_resource(AgentCollection, '/api/agent/', endpoint='api_agents')


class Agent(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(player_list_field)
    def get(self, id_):
        self.parser.add_argument(
            'page',
            type=inputs.natural,
            default=0
        )
        args = self.parser.parse_args()
        page = args['page']
        page_len = current_app.config['PAGE_LENGTH']

        query = models.Player.query.join(
            models.AgentPlayer, models.Player.wxid == models.AgentPlayer.player_wxid
        ).filter(models.AgentPlayer.agent_wxid == id_)
        count = query.count()
        players = query.order_by(
            models.Player.wager_amount.desc()
        ).limit(page_len).offset(page*page_len).all()
        return {'players': players, 'count': count}


manager_api.add_resource(Agent, '/api/agent/<id_>', endpoint='api_agent')


class AgentPlayer(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    def put(self, player_wxid):
        if not current_user.is_admin:
            return {'message': '仅限管理员操作'}, 403
        self.parser.add_argument(
            'agentWxid',
            type=str,
            required=True,
            location='get_json',
            help='Missing agent wxid'
        )
        args = self.parser.parse_args()
        agent_wxid = args['agentWxid']
        ap = models.AgentPlayer.create(player_wxid, agent_wxid)


manager_api.add_resource(AgentPlayer, '/api/agent_player/<player_wxid>', endpoint='api_agent_player')


except_grabber_list_field = {
    'grabbers': fields.List(fields.Nested(models.Marshal.exceptional_grabber)),
    'count': fields.Integer,
}


class ExceptionalGrabberCollection(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(except_grabber_list_field)
    def get(self):
        grabbers = models.ExceptionalGrabber.query.all()
        return {'grabbers': grabbers, 'count': len(grabbers)}

    def post(self):
        if not current_user.is_admin:
            return {'message': '仅限管理员操作'}, 403
        self.parser.add_argument(
            'wxid',
            type=str,
            required=True,
            location='get_json',
        )
        args = self.parser.parse_args()
        wxid = args['wxid']
        success, grabber_or_msg = models.ExceptionalGrabber.create(wxid)
        if not success:
            return {"message": grabber_or_msg}, 400
        return {"message": "add grabber successfully"}

    def delete(self):
        if not current_user.is_admin:
            return {'message': '仅限管理员操作'}, 403
        self.parser.add_argument(
            'wxid',
            type=str,
            required=True,
            location='get_json',
        )
        args = self.parser.parse_args()
        wxid = args['wxid']
        grabber = models.ExceptionalGrabber.query.filter(models.ExceptionalGrabber.wxid == wxid).first()
        if grabber is None:
            return {"message": 'not an agent'}, 403
        error = grabber.revoke_grabber()
        if error:
            return {"message": error}, 500
        else:
            return {"message": "revoke grabber successfully"}


manager_api.add_resource(ExceptionalGrabberCollection, '/api/except_grabber/', endpoint='api_except_grabber')


class Financing(Resource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parser = reqparse.RequestParser()

    @marshal_with(models.Marshal.financing, envelope='financing')
    def get(self):
        try:
            stat = models.Statistics.get_instance()
        except:
            import traceback
            print(traceback.format_exc())
        return stat


manager_api.add_resource(Financing, '/api/financing', endpoint='api_financing')
